import sys
import os
import time
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout,
                             QHBoxLayout, QFileDialog, QSlider, QWidget, QMessageBox,
                             QAction, QMenuBar, QToolBar, QStatusBar, QComboBox, QLineEdit,
                             QInputDialog, QDialog, QGroupBox, QTabWidget, QFrame)
from PyQt5.QtGui import QPixmap, QImage, QIcon, QIntValidator, QPainter, QPen, QColor, QCursor
from PyQt5.QtCore import Qt, QSize, QPoint, QRect, QThread, pyqtSignal, QTimer
import numpy as np
from PIL import Image, ImageFilter, ImageEnhance
import cv2
from io import BytesIO


# 修复Qt平台插件问题
def fix_qt_platform():
    if hasattr(sys, '_MEIPASS'):  # 处理打包后的应用
        os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(sys._MEIPASS, 'PyQt5', 'Qt', 'plugins', 'platforms')
    else:  # 开发环境
        try:
            import PyQt5
            pyqt_path = os.path.dirname(PyQt5.__file__)
            plugin_path = os.path.join(pyqt_path, 'Qt', 'plugins', 'platforms')
            if os.path.exists(plugin_path):
                os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path
        except ImportError:
            pass


fix_qt_platform()


class VideoProcessor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.original_img = None
        self.processed_img = None
        self.current_img = None
        self.file_path = None

        # 视频处理相关变量
        self.video_capture = None
        self.video_timer = None
        self.is_playing = False
        self.video_fps = 30
        self.current_frame = 0
        self.total_frames = 0

        # 摄像头处理相关变量
        self.camera_capture = None
        self.camera_timer = None
        self.is_camera_active = False
        self.camera_id = 0

        # 色彩调整参数
        self.brightness = 1.0  # 亮度系数（1.0为原始值）
        self.contrast = 1.0  # 对比度系数（1.0为原始值）
        self.saturation = 1.0  # 饱和度系数（1.0为原始值）

        # 裁剪相关变量
        self.cropping = False
        self.crop_start = QPoint()
        self.crop_end = QPoint()

        # 平滑处理参数
        self.smoothing_type = "均值滤波"  # 默认平滑类型
        self.smoothing_size = 3  # 默认平滑核大小

        # 图像拼接相关
        self.stitch_images = []  # 存储待拼接的图像列表
        self.stitcher = cv2.Stitcher_create()  # OpenCV拼接器

        self.setup_ui()

    def setup_ui(self):
        # 设置窗口
        self.setWindowTitle("图像处理与视频分析工具")
        self.setGeometry(100, 100, 1200, 800)

        # 创建菜单栏
        self.create_menu_bar()

        # 创建工具栏
        self.create_tool_bar()

        # 创建主布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        main_layout = QHBoxLayout(main_widget)

        # 创建标签页控件
        self.tab_widget = QTabWidget()

        # 图像处理标签页
        self.image_tab = QWidget()
        self.setup_image_tab()
        self.tab_widget.addTab(self.image_tab, "图像处理")

        # 视频处理标签页
        self.video_tab = QWidget()
        self.setup_video_tab()
        self.tab_widget.addTab(self.video_tab, "视频处理")

        # 摄像头处理标签页
        self.camera_tab = QWidget()
        self.setup_camera_tab()
        self.tab_widget.addTab(self.camera_tab, "摄像头处理")

        main_layout.addWidget(self.tab_widget, 7)

    def setup_image_tab(self):
        # 创建图像处理标签页布局
        image_layout = QVBoxLayout(self.image_tab)

        # 分割左右区域
        content_layout = QHBoxLayout()

        # 左侧图像显示区域
        left_layout = QVBoxLayout()

        self.original_label = QLabel("原始图像")
        self.original_label.setAlignment(Qt.AlignCenter)
        self.original_label.setMinimumSize(400, 400)
        self.original_label.setStyleSheet("border: 1px solid #cccccc;")
        self.original_label.mousePressEvent = self.mouse_press_event
        self.original_label.mouseMoveEvent = self.mouse_move_event
        self.original_label.mouseReleaseEvent = self.mouse_release_event

        self.processed_label = QLabel("处理后图像")
        self.processed_label.setAlignment(Qt.AlignCenter)
        self.processed_label.setMinimumSize(400, 400)
        self.processed_label.setStyleSheet("border: 1px solid #cccccc;")

        left_layout.addWidget(self.original_label)
        left_layout.addWidget(self.processed_label)

        # 右侧控制面板
        right_layout = QVBoxLayout()

        # --------------------- 亮度/对比度/饱和度调整 ---------------------
        color_layout = QVBoxLayout()
        color_group = QGroupBox("色彩调整")

        # 亮度调整
        brightness_layout = QHBoxLayout()
        brightness_label = QLabel("亮度:")
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(0, 200)  # 0-200对应0.0-2.0倍
        self.brightness_slider.setValue(100)  # 初始值为1.0倍
        self.brightness_slider.valueChanged.connect(self.adjust_brightness)
        self.brightness_value = QLabel("1.0")
        brightness_layout.addWidget(brightness_label)
        brightness_layout.addWidget(self.brightness_slider)
        brightness_layout.addWidget(self.brightness_value)

        # 对比度调整
        contrast_layout = QHBoxLayout()
        contrast_label = QLabel("对比度:")
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(0, 200)  # 0-200对应0.0-2.0倍
        self.contrast_slider.setValue(100)  # 初始值为1.0倍
        self.contrast_slider.valueChanged.connect(self.adjust_contrast)
        self.contrast_value = QLabel("1.0")
        contrast_layout.addWidget(contrast_label)
        contrast_layout.addWidget(self.contrast_slider)
        contrast_layout.addWidget(self.contrast_value)

        # 饱和度调整
        saturation_layout = QHBoxLayout()
        saturation_label = QLabel("饱和度:")
        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(0, 200)  # 0-200对应0.0-2.0倍
        self.saturation_slider.setValue(100)  # 初始值为1.0倍
        self.saturation_slider.valueChanged.connect(self.adjust_saturation)
        self.saturation_value = QLabel("1.0")
        saturation_layout.addWidget(saturation_label)
        saturation_layout.addWidget(self.saturation_slider)
        saturation_layout.addWidget(self.saturation_value)

        color_group.setLayout(QVBoxLayout())
        color_group.layout().addLayout(brightness_layout)
        color_group.layout().addLayout(contrast_layout)
        color_group.layout().addLayout(saturation_layout)
        right_layout.addWidget(color_group)

        # --------------------- 其他图像处理操作 ---------------------
        operations_layout = QHBoxLayout()
        operations_label = QLabel("图像处理:")
        self.operations_combo = QComboBox()
        self.operations_combo.addItems([
            "原图", "灰度图", "黑白图", "模糊", "锐化",
            "边缘检测", "浮雕", "旋转90°", "旋转180°", "镜像", "平滑处理"
        ])
        self.operations_combo.currentIndexChanged.connect(self.apply_operation)
        operations_layout.addWidget(operations_label)
        operations_layout.addWidget(self.operations_combo)

        # 平滑处理参数调整 - 使用QWidget包装
        self.smoothing_widget = QWidget()
        self.smoothing_layout = QHBoxLayout(self.smoothing_widget)

        self.smoothing_type_label = QLabel("平滑类型:")
        self.smoothing_type_combo = QComboBox()
        self.smoothing_type_combo.addItems(["均值滤波", "高斯滤波", "中值滤波"])
        self.smoothing_type_combo.currentIndexChanged.connect(self.update_smoothing_params)

        self.smoothing_size_label = QLabel("平滑强度:")
        self.smoothing_size_slider = QSlider(Qt.Horizontal)
        self.smoothing_size_slider.setRange(1, 15)  # 核大小范围
        self.smoothing_size_slider.setValue(3)  # 默认值
        self.smoothing_size_slider.setSingleStep(2)  # 只允许奇数
        self.smoothing_size_slider.valueChanged.connect(self.update_smoothing_params)

        self.smoothing_size_value = QLabel("3")

        self.smoothing_layout.addWidget(self.smoothing_type_label)
        self.smoothing_layout.addWidget(self.smoothing_type_combo)
        self.smoothing_layout.addWidget(self.smoothing_size_label)
        self.smoothing_layout.addWidget(self.smoothing_size_slider)
        self.smoothing_layout.addWidget(self.smoothing_size_value)

        right_layout.addWidget(self.smoothing_widget)

        # 旋转角度调整
        rotation_layout = QHBoxLayout()
        rotation_label = QLabel("旋转角度:")
        self.rotation_input = QLineEdit()
        self.rotation_input.setPlaceholderText("输入角度")
        self.rotation_input.setValidator(QIntValidator(0, 360))
        self.rotation_input.setText("0")
        self.rotate_button = QPushButton("旋转")
        self.rotate_button.clicked.connect(self.rotate_image)
        rotation_layout.addWidget(rotation_label)
        rotation_layout.addWidget(self.rotation_input)
        rotation_layout.addWidget(self.rotate_button)

        # 缩放调整
        scale_layout = QHBoxLayout()
        scale_label = QLabel("缩放比例(%):")
        self.scale_input = QLineEdit()
        self.scale_input.setPlaceholderText("输入百分比")
        self.scale_input.setValidator(QIntValidator(10, 500))
        self.scale_input.setText("100")
        self.scale_button = QPushButton("缩放")
        self.scale_button.clicked.connect(self.scale_image)
        scale_layout.addWidget(scale_label)
        scale_layout.addWidget(self.scale_input)
        scale_layout.addWidget(self.scale_button)

        # 裁剪按钮
        crop_layout = QHBoxLayout()
        self.crop_button = QPushButton("开始裁剪")
        self.crop_button.clicked.connect(self.start_crop)
        self.apply_crop_button = QPushButton("应用裁剪")
        self.apply_crop_button.clicked.connect(self.apply_crop)
        self.apply_crop_button.setEnabled(False)
        crop_layout.addWidget(self.crop_button)
        crop_layout.addWidget(self.apply_crop_button)

        # 添加形状识别功能
        shapes_layout = QHBoxLayout()
        shapes_label = QLabel("形状识别:")
        self.shapes_combo = QComboBox()
        self.shapes_combo.addItems([
            "关闭", "检测圆形", "检测矩形", "检测三角形", "检测多边形", "自动检测所有"
        ])
        self.shapes_combo.currentIndexChanged.connect(self.detect_shapes)
        shapes_layout.addWidget(shapes_label)
        shapes_layout.addWidget(self.shapes_combo)

        # 形状识别参数调整
        self.shapes_widget = QWidget()
        self.shapes_layout = QHBoxLayout(self.shapes_widget)

        self.shape_threshold_label = QLabel("检测阈值:")
        self.shape_threshold_slider = QSlider(Qt.Horizontal)
        self.shape_threshold_slider.setRange(50, 200)  # 阈值范围
        self.shape_threshold_slider.setValue(100)  # 默认值
        self.shape_threshold_slider.valueChanged.connect(self.detect_shapes)

        self.shape_threshold_value = QLabel("100")

        self.shapes_layout.addWidget(self.shape_threshold_label)
        self.shapes_layout.addWidget(self.shape_threshold_slider)
        self.shapes_layout.addWidget(self.shape_threshold_value)

        # 添加到右侧布局
        right_layout.addLayout(shapes_layout)
        right_layout.addWidget(self.shapes_widget)

        # 初始隐藏形状识别参数控件
        self.shapes_widget.hide()

        # 连接信号以显示/隐藏形状识别参数控件
        self.shapes_combo.currentIndexChanged.connect(self.on_shapes_operation_changed)

        # --------------------- 图像拼接功能 ---------------------
        stitch_layout = QHBoxLayout()
        self.stitch_button = QPushButton("选择拼接图像")
        self.stitch_button.clicked.connect(self.select_stitch_images)
        self.start_stitch_button = QPushButton("开始拼接")
        self.start_stitch_button.clicked.connect(self.perform_stitching)
        self.start_stitch_button.setEnabled(False)
        stitch_layout.addWidget(self.stitch_button)
        stitch_layout.addWidget(self.start_stitch_button)

        # 添加到右侧布局
        right_layout.addLayout(stitch_layout)

        # 按钮
        buttons_layout = QHBoxLayout()
        self.reset_button = QPushButton("重置")
        self.reset_button.clicked.connect(self.reset_image)
        self.save_button = QPushButton("保存")
        self.save_button.clicked.connect(self.save_image)
        self.save_button.setEnabled(False)
        buttons_layout.addWidget(self.reset_button)
        buttons_layout.addWidget(self.save_button)

        # 添加所有控件到右侧布局
        right_layout.addLayout(operations_layout)
        right_layout.addLayout(rotation_layout)
        right_layout.addLayout(scale_layout)
        right_layout.addLayout(crop_layout)
        right_layout.addLayout(buttons_layout)
        right_layout.addStretch()

        # 添加左右布局到主布局
        content_layout.addLayout(left_layout, 7)
        content_layout.addLayout(right_layout, 3)

        image_layout.addLayout(content_layout)

        # 创建状态栏
        self.statusBar().showMessage("就绪")

        # 初始隐藏平滑参数控件
        self.smoothing_widget.hide()

        # 连接信号以显示/隐藏平滑参数控件
        self.operations_combo.currentIndexChanged.connect(self.on_operation_changed)

    def setup_video_tab(self):
        # 创建视频处理标签页布局
        video_layout = QVBoxLayout(self.video_tab)

        # 视频显示区域
        video_display_layout = QHBoxLayout()

        self.video_original_label = QLabel("原始视频")
        self.video_original_label.setAlignment(Qt.AlignCenter)
        self.video_original_label.setMinimumSize(500, 300)
        self.video_original_label.setStyleSheet("border: 1px solid #cccccc;")

        self.video_processed_label = QLabel("处理后视频")
        self.video_processed_label.setAlignment(Qt.AlignCenter)
        self.video_processed_label.setMinimumSize(500, 300)
        self.video_processed_label.setStyleSheet("border: 1px solid #cccccc;")

        video_display_layout.addWidget(self.video_original_label)
        video_display_layout.addWidget(self.video_processed_label)

        # 视频控制区域
        video_control_layout = QVBoxLayout()

        # 视频文件选择
        file_select_layout = QHBoxLayout()
        self.video_file_label = QLabel("未选择视频文件")
        self.select_video_button = QPushButton("选择视频")
        self.select_video_button.clicked.connect(self.select_video_file)
        file_select_layout.addWidget(self.video_file_label)
        file_select_layout.addWidget(self.select_video_button)

        # 视频控制按钮
        control_buttons_layout = QHBoxLayout()
        self.play_button = QPushButton("播放")
        self.play_button.clicked.connect(self.play_video)

        self.pause_button = QPushButton("暂停")
        self.pause_button.clicked.connect(self.pause_video)
        self.pause_button.setEnabled(False)

        self.stop_button = QPushButton("停止")
        self.stop_button.clicked.connect(self.stop_video)
        self.stop_button.setEnabled(False)

        self.forward_button = QPushButton("快进")
        self.forward_button.clicked.connect(self.forward_video)

        self.backward_button = QPushButton("快退")
        self.backward_button.clicked.connect(self.backward_video)

        control_buttons_layout.addWidget(self.play_button)
        control_buttons_layout.addWidget(self.pause_button)
        control_buttons_layout.addWidget(self.stop_button)
        control_buttons_layout.addWidget(self.backward_button)
        control_buttons_layout.addWidget(self.forward_button)

        # 进度条
        progress_layout = QHBoxLayout()
        self.video_progress = QSlider(Qt.Horizontal)
        self.video_progress.setRange(0, 1000)
        self.video_progress.setValue(0)
        self.video_progress.sliderMoved.connect(self.set_video_position)

        self.time_label = QLabel("00:00/00:00")

        progress_layout.addWidget(self.video_progress)
        progress_layout.addWidget(self.time_label)

        # 视频处理选项
        video_processing_layout = QHBoxLayout()
        processing_label = QLabel("视频处理:")
        self.video_processing_combo = QComboBox()
        self.video_processing_combo.addItems([
            "无处理", "灰度图", "边缘检测", "形状识别"
        ])
        self.video_processing_combo.currentIndexChanged.connect(self.set_video_processing)

        video_processing_layout.addWidget(processing_label)
        video_processing_layout.addWidget(self.video_processing_combo)

        # 形状识别参数
        self.video_shape_widget = QWidget()
        video_shape_layout = QHBoxLayout(self.video_shape_widget)

        shape_threshold_label = QLabel("形状阈值:")
        self.video_shape_threshold = QSlider(Qt.Horizontal)
        self.video_shape_threshold.setRange(50, 200)
        self.video_shape_threshold.setValue(100)
        self.video_shape_threshold.valueChanged.connect(self.set_video_shape_threshold)

        self.video_shape_threshold_value = QLabel("100")

        video_shape_layout.addWidget(shape_threshold_label)
        video_shape_layout.addWidget(self.video_shape_threshold)
        video_shape_layout.addWidget(self.video_shape_threshold_value)

        # 添加所有控件到视频控制布局
        video_control_layout.addLayout(file_select_layout)
        video_control_layout.addLayout(control_buttons_layout)
        video_control_layout.addLayout(progress_layout)
        video_control_layout.addLayout(video_processing_layout)
        video_control_layout.addLayout(video_shape_layout)

        # 初始隐藏形状识别参数
        self.video_shape_widget.hide()

        # 添加所有布局到视频标签页
        video_layout.addLayout(video_display_layout)
        video_layout.addLayout(video_control_layout)

        # 初始化视频定时器
        self.video_timer = QTimer(self)
        self.video_timer.timeout.connect(self.update_video_frame)

        # 视频处理类型
        self.video_processing_type = "无处理"
        self.video_shape_threshold_value = 100

    def setup_camera_tab(self):
        # 创建摄像头处理标签页布局
        camera_layout = QVBoxLayout(self.camera_tab)

        # 摄像头显示区域
        camera_display_layout = QHBoxLayout()

        self.camera_original_label = QLabel("原始摄像头")
        self.camera_original_label.setAlignment(Qt.AlignCenter)
        self.camera_original_label.setMinimumSize(500, 300)
        self.camera_original_label.setStyleSheet("border: 1px solid #cccccc;")

        self.camera_processed_label = QLabel("处理后摄像头")
        self.camera_processed_label.setAlignment(Qt.AlignCenter)
        self.camera_processed_label.setMinimumSize(500, 300)
        self.camera_processed_label.setStyleSheet("border: 1px solid #cccccc;")

        camera_display_layout.addWidget(self.camera_original_label)
        camera_display_layout.addWidget(self.camera_processed_label)

        # 摄像头控制区域
        camera_control_layout = QVBoxLayout()

        # 摄像头选择
        camera_select_layout = QHBoxLayout()
        camera_label = QLabel("摄像头:")
        self.camera_id_combo = QComboBox()
        self.camera_id_combo.addItems(["摄像头 0", "摄像头 1", "摄像头 2"])
        self.camera_id_combo.currentIndexChanged.connect(self.change_camera)

        self.start_camera_button = QPushButton("开始摄像头")
        self.start_camera_button.clicked.connect(self.start_camera)

        self.stop_camera_button = QPushButton("停止摄像头")
        self.stop_camera_button.clicked.connect(self.stop_camera)
        self.stop_camera_button.setEnabled(False)

        camera_select_layout.addWidget(camera_label)
        camera_select_layout.addWidget(self.camera_id_combo)
        camera_select_layout.addWidget(self.start_camera_button)
        camera_select_layout.addWidget(self.stop_camera_button)

        # 摄像头处理选项
        camera_processing_layout = QHBoxLayout()
        processing_label = QLabel("摄像头处理:")
        self.camera_processing_combo = QComboBox()
        self.camera_processing_combo.addItems([
            "无处理", "灰度图", "边缘检测", "形状识别", "人脸检测"
        ])
        self.camera_processing_combo.currentIndexChanged.connect(self.set_camera_processing)

        camera_processing_layout.addWidget(processing_label)
        camera_processing_layout.addWidget(self.camera_processing_combo)

        # 形状识别参数
        self.camera_shape_widget = QWidget()
        camera_shape_layout = QHBoxLayout(self.camera_shape_widget)

        shape_threshold_label = QLabel("形状阈值:")
        self.camera_shape_threshold = QSlider(Qt.Horizontal)
        self.camera_shape_threshold.setRange(50, 200)
        self.camera_shape_threshold.setValue(100)
        self.camera_shape_threshold.valueChanged.connect(self.set_camera_shape_threshold)

        self.camera_shape_threshold_value = QLabel("100")

        camera_shape_layout.addWidget(shape_threshold_label)
        camera_shape_layout.addWidget(self.camera_shape_threshold)
        camera_shape_layout.addWidget(self.camera_shape_threshold_value)

        # 人脸检测参数
        self.camera_face_widget = QWidget()
        camera_face_layout = QHBoxLayout(self.camera_face_widget)

        face_scale_label = QLabel("人脸缩放因子:")
        self.camera_face_scale = QSlider(Qt.Horizontal)
        self.camera_face_scale.setRange(11, 20)  # 对应1.1-2.0
        self.camera_face_scale.setValue(13)  # 默认1.3
        self.camera_face_scale.valueChanged.connect(self.set_camera_face_scale)

        self.camera_face_scale_value = QLabel("1.3")

        face_neighbors_label = QLabel("人脸邻居数:")
        self.camera_face_neighbors = QSlider(Qt.Horizontal)
        self.camera_face_neighbors.setRange(1, 10)
        self.camera_face_neighbors.setValue(5)
        self.camera_face_neighbors.valueChanged.connect(self.set_camera_face_neighbors)

        self.camera_face_neighbors_value = QLabel("5")

        camera_face_layout.addWidget(face_scale_label)
        camera_face_layout.addWidget(self.camera_face_scale)
        camera_face_layout.addWidget(self.camera_face_scale_value)
        camera_face_layout.addWidget(face_neighbors_label)
        camera_face_layout.addWidget(self.camera_face_neighbors)
        camera_face_layout.addWidget(self.camera_face_neighbors_value)

        # 添加所有控件到摄像头控制布局
        camera_control_layout.addLayout(camera_select_layout)
        camera_control_layout.addLayout(camera_processing_layout)
        camera_control_layout.addLayout(camera_shape_layout)
        camera_control_layout.addLayout(camera_face_layout)

        # 初始隐藏形状识别和人脸检测参数
        self.camera_shape_widget.hide()
        self.camera_face_widget.hide()

        # 添加所有布局到摄像头标签页
        camera_layout.addLayout(camera_display_layout)
        camera_layout.addLayout(camera_control_layout)

        # 初始化摄像头定时器
        self.camera_timer = QTimer(self)
        self.camera_timer.timeout.connect(self.update_camera_frame)

        # 摄像头处理类型
        self.camera_processing_type = "无处理"
        self.camera_shape_threshold_value = 100
        self.camera_face_scale_value = 1.3
        self.camera_face_neighbors_value = 5

        # 加载人脸检测器
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

    def create_menu_bar(self):
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu("文件")

        open_action = QAction("打开图像", self)
        open_action.setShortcut("Ctrl+O")
        open_action.triggered.connect(self.open_image)
        file_menu.addAction(open_action)

        open_video_action = QAction("打开视频", self)
        open_video_action.setShortcut("Ctrl+V")
        open_video_action.triggered.connect(self.select_video_file)
        file_menu.addAction(open_video_action)

        save_action = QAction("保存", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_image)
        save_action.setEnabled(False)
        file_menu.addAction(save_action)

        file_menu.addSeparator()

        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menubar.addMenu("编辑")

        reset_action = QAction("重置", self)
        reset_action.setShortcut("Ctrl+R")
        reset_action.triggered.connect(self.reset_image)
        edit_menu.addAction(reset_action)

        # 处理菜单
        process_menu = menubar.addMenu("处理")

        # 添加图像处理操作
        process_submenu = menubar.addMenu("图像操作")
        for op in ["灰度图", "黑白图", "模糊", "锐化", "边缘检测", "浮雕", "旋转90°", "旋转180°", "镜像"]:
            action = QAction(op, self)
            action.triggered.connect(lambda checked, x=op: self.apply_menu_operation(x))
            process_submenu.addAction(action)

        # 帮助菜单
        help_menu = menubar.addMenu("帮助")

        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def create_tool_bar(self):
        toolbar = QToolBar("工具栏")
        self.addToolBar(toolbar)

        open_icon = QIcon.fromTheme("document-open")
        open_action = toolbar.addAction(open_icon, "打开图像")
        open_action.triggered.connect(self.open_image)

        open_video_icon = QIcon.fromTheme("media-optical")
        open_video_action = toolbar.addAction(open_video_icon, "打开视频")
        open_video_action.triggered.connect(self.select_video_file)

        save_icon = QIcon.fromTheme("document-save")
        save_action = toolbar.addAction(save_icon, "保存")
        save_action.setEnabled(False)
        save_action.triggered.connect(self.save_image)

        reset_icon = QIcon.fromTheme("edit-undo")
        reset_action = toolbar.addAction(reset_icon, "重置")
        reset_action.triggered.connect(self.reset_image)

        toolbar.addSeparator()

        help_icon = QIcon.fromTheme("help-contents")
        help_action = toolbar.addAction(help_icon, "帮助")
        help_action.triggered.connect(self.show_about)

    # 图像处理相关方法
    def open_image(self):
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif);;所有文件 (*)", options=options
        )

        if file_path:
            self.file_path = file_path
            self.original_img = Image.open(file_path).convert("RGB")
            self.processed_img = self.original_img.copy()
            self.current_img = self.processed_img.copy()
            self.reset_color_settings()  # 打开新图像时重置色彩参数
            self.update_image_display()
            self.save_button.setEnabled(True)
            self.statusBar().showMessage(f"已打开: {os.path.basename(file_path)}")
            # 重置滑块和输入框
            self.brightness_slider.setValue(100)
            self.contrast_slider.setValue(100)
            self.operations_combo.setCurrentIndex(0)
            self.rotation_input.setText("0")
            self.scale_input.setText("100")
            # 重置裁剪状态
            self.cropping = False
            self.crop_start = QPoint()
            self.crop_end = QPoint()
            self.apply_crop_button.setEnabled(False)
            # 重置平滑参数
            self.smoothing_type_combo.setCurrentIndex(0)
            self.smoothing_size_slider.setValue(3)
            # 重置形状识别参数
            self.shapes_combo.setCurrentIndex(0)
            self.shape_threshold_slider.setValue(100)
            # 重置拼接状态
            self.stitch_images = []
            self.start_stitch_button.setEnabled(False)

    def reset_color_settings(self):
        """重置色彩调整参数为默认值"""
        self.brightness = 1.0
        self.contrast = 1.0
        self.saturation = 1.0
        self.brightness_slider.setValue(100)
        self.contrast_slider.setValue(100)
        self.saturation_slider.setValue(100)
        self.brightness_value.setText("1.0")
        self.contrast_value.setText("1.0")
        self.saturation_value.setText("1.0")

    def adjust_brightness(self, value):
        """亮度调整回调函数"""
        self.brightness = value / 100.0
        self.brightness_value.setText(f"{self.brightness:.1f}")
        self.apply_color_adjustments()

    def adjust_contrast(self, value):
        """对比度调整回调函数"""
        self.contrast = value / 100.0
        self.contrast_value.setText(f"{self.contrast:.1f}")
        self.apply_color_adjustments()

    def adjust_saturation(self, value):
        """饱和度调整回调函数"""
        self.saturation = value / 100.0
        self.saturation_value.setText(f"{self.saturation:.1f}")
        self.apply_color_adjustments()

    def apply_color_adjustments(self):
        """应用色彩调整到当前图像"""
        if self.processed_img:
            # 亮度调整
            img = ImageEnhance.Brightness(self.processed_img).enhance(self.brightness)
            # 对比度调整
            img = ImageEnhance.Contrast(img).enhance(self.contrast)
            # 饱和度调整（需转换为HSV空间处理）
            img_hsv = img.convert("HSV")
            h, s, v = img_hsv.split()
            s = s.point(lambda p: p * self.saturation)  # 调整饱和度通道
            img_hsv = Image.merge("HSV", (h, s, v))
            self.current_img = img_hsv.convert("RGB")
            self.update_image_display()

    def update_smoothing_params(self):
        """更新平滑处理参数"""
        self.smoothing_type = self.smoothing_type_combo.currentText()
        self.smoothing_size = self.smoothing_size_slider.value()
        self.smoothing_size_value.setText(str(self.smoothing_size))

        # 确保平滑大小是奇数
        if self.smoothing_size % 2 == 0:
            self.smoothing_size += 1
            self.smoothing_size_slider.setValue(self.smoothing_size)

        # 如果当前选择的是平滑处理，则应用更改
        if self.operations_combo.currentText() == "平滑处理":
            self.apply_operation(self.operations_combo.currentIndex())

    def apply_operation(self, index):
        """图像处理操作"""
        if not self.processed_img:
            return

        operation = self.operations_combo.currentText()

        if operation == "原图":
            self.current_img = self.processed_img.copy()

        elif operation == "灰度图":
            self.current_img = self.processed_img.convert("L").convert("RGB")

        elif operation == "黑白图":
            img_np = np.array(self.processed_img.convert("L"))
            _, binary = cv2.threshold(img_np, 127, 255, cv2.THRESH_BINARY)
            self.current_img = Image.fromarray(binary).convert("RGB")

        elif operation == "模糊":
            self.current_img = self.processed_img.filter(ImageFilter.BLUR)

        elif operation == "锐化":
            self.current_img = self.processed_img.filter(ImageFilter.SHARPEN)

        elif operation == "边缘检测":
            self.current_img = self.processed_img.filter(ImageFilter.FIND_EDGES)

        elif operation == "浮雕":
            self.current_img = self.processed_img.filter(ImageFilter.EMBOSS)

        elif operation == "旋转90°":
            self.current_img = self.processed_img.rotate(90, expand=True)

        elif operation == "旋转180°":
            self.current_img = self.processed_img.rotate(180, expand=True)

        elif operation == "镜像":
            self.current_img = self.processed_img.transpose(Image.FLIP_LEFT_RIGHT)

        elif operation == "平滑处理":
            # 转换为OpenCV格式进行处理
            img_cv = cv2.cvtColor(np.array(self.processed_img), cv2.COLOR_RGB2BGR)

            if self.smoothing_type == "均值滤波":
                img_cv = cv2.blur(img_cv, (self.smoothing_size, self.smoothing_size))

            elif self.smoothing_type == "高斯滤波":
                img_cv = cv2.GaussianBlur(img_cv, (self.smoothing_size, self.smoothing_size), 0)

            elif self.smoothing_type == "中值滤波":
                img_cv = cv2.medianBlur(img_cv, self.smoothing_size)

            # 转回PIL格式
            self.current_img = Image.fromarray(cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB))
            self.statusBar().showMessage(f"已应用{self.smoothing_type} (核大小: {self.smoothing_size})")

        # 应用亮度和对比度调整
        self.apply_color_adjustments()

    def apply_menu_operation(self, operation):
        """从菜单应用图像处理操作"""
        if not self.processed_img:
            return

        if operation == "灰度图":
            self.current_img = self.processed_img.convert("L").convert("RGB")
        elif operation == "黑白图":
            img_np = np.array(self.processed_img.convert("L"))
            _, binary = cv2.threshold(img_np, 127, 255, cv2.THRESH_BINARY)
            self.current_img = Image.fromarray(binary).convert("RGB")
        elif operation == "模糊":
            self.current_img = self.processed_img.filter(ImageFilter.BLUR)
        elif operation == "锐化":
            self.current_img = self.processed_img.filter(ImageFilter.SHARPEN)
        elif operation == "边缘检测":
            self.current_img = self.processed_img.filter(ImageFilter.FIND_EDGES)
        elif operation == "浮雕":
            self.current_img = self.processed_img.filter(ImageFilter.EMBOSS)
        elif operation == "旋转90°":
            self.current_img = self.processed_img.rotate(90, expand=True)
        elif operation == "旋转180°":
            self.current_img = self.processed_img.rotate(180, expand=True)
        elif operation == "镜像":
            self.current_img = self.processed_img.transpose(Image.FLIP_LEFT_RIGHT)

        # 更新显示
        self.update_image_display()
        self.statusBar().showMessage(f"已应用: {operation}")

    def on_operation_changed(self, index):
        """当操作选择改变时显示或隐藏相关参数控件"""
        operation = self.operations_combo.currentText()
        if operation == "平滑处理":
            self.smoothing_widget.show()
        else:
            self.smoothing_widget.hide()

    def rotate_image(self):
        """旋转图像"""
        if not self.processed_img:
            return

        try:
            angle = int(self.rotation_input.text())
            self.current_img = self.processed_img.rotate(angle, expand=True)
            self.update_image_display()
            self.statusBar().showMessage(f"已旋转 {angle} 度")
        except ValueError:
            QMessageBox.warning(self, "输入错误", "请输入有效的角度值 (0-360)")

    def scale_image(self):
        """缩放图像"""
        if not self.processed_img:
            return

        try:
            percent = int(self.scale_input.text())
            if percent <= 0:
                raise ValueError("百分比必须大于0")

            width, height = self.processed_img.size
            new_width = int(width * percent / 100)
            new_height = int(height * percent / 100)
            self.current_img = self.processed_img.resize((new_width, new_height), Image.LANCZOS)
            self.update_image_display()
            self.statusBar().showMessage(f"已缩放至 {percent}%")
        except ValueError as e:
            QMessageBox.warning(self, "输入错误", str(e))

    def mouse_press_event(self, event):
        """鼠标按下事件 - 开始裁剪"""
        if self.cropping and event.button() == Qt.LeftButton:
            self.crop_start = event.pos()
            self.crop_end = event.pos()
            self.update_image_display()

    def mouse_move_event(self, event):
        """鼠标移动事件 - 更新裁剪区域"""
        if self.cropping and event.buttons() & Qt.LeftButton:
            self.crop_end = event.pos()
            self.update_image_display()

    def mouse_release_event(self, event):
        """鼠标释放事件 - 结束裁剪"""
        if self.cropping and event.button() == Qt.LeftButton:
            self.crop_end = event.pos()
            self.apply_crop_button.setEnabled(True)
            self.update_image_display()

    def start_crop(self):
        """开始裁剪"""
        if not self.processed_img:
            return

        self.cropping = True
        self.crop_button.setEnabled(False)
        self.apply_crop_button.setEnabled(False)
        self.statusBar().showMessage("请在图像上拖动鼠标选择裁剪区域")

    def apply_crop(self):
        """应用裁剪"""
        if not self.cropping or not self.processed_img:
            return

        # 确保起点和终点正确
        x1 = min(self.crop_start.x(), self.crop_end.x())
        y1 = min(self.crop_start.y(), self.crop_end.y())
        x2 = max(self.crop_start.x(), self.crop_end.x())
        y2 = max(self.crop_start.y(), self.crop_end.y())

        # 确保坐标在图像范围内
        img_width, img_height = self.processed_img.size
        x1 = max(0, min(x1, img_width))
        y1 = max(0, min(y1, img_height))
        x2 = max(0, min(x2, img_width))
        y2 = max(0, min(y2, img_height))

        if x2 <= x1 or y2 <= y1:
            QMessageBox.warning(self, "裁剪错误", "请选择有效的裁剪区域")
            return

        # 裁剪图像
        self.processed_img = self.processed_img.crop((x1, y1, x2, y2))
        self.current_img = self.processed_img.copy()

        # 重置裁剪状态
        self.cropping = False
        self.crop_start = QPoint()
        self.crop_end = QPoint()
        self.crop_button.setEnabled(True)
        self.apply_crop_button.setEnabled(False)

        # 更新显示
        self.update_image_display()
        self.statusBar().showMessage("已完成裁剪")

    def update_image_display(self):
        """更新图像显示"""
        if self.original_img:
            # 显示原始图像
            original_pixmap = self.convert_pil_to_pixmap(self.original_img)
            self.original_label.setPixmap(original_pixmap.scaled(
                self.original_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

            # 显示处理后的图像
            if self.current_img:
                processed_pixmap = self.convert_pil_to_pixmap(self.current_img)

                # 如果正在裁剪，绘制裁剪矩形
                if self.cropping and not self.crop_start.isNull() and not self.crop_end.isNull():
                    processed_pixmap = processed_pixmap.copy()
                    painter = QPainter(processed_pixmap)
                    painter.setPen(QPen(Qt.red, 2, Qt.DashLine))

                    # 计算裁剪矩形的坐标
                    x1 = min(self.crop_start.x(), self.crop_end.x())
                    y1 = min(self.crop_start.y(), self.crop_end.y())
                    x2 = max(self.crop_start.x(), self.crop_end.x())
                    y2 = max(self.crop_start.y(), self.crop_end.y())

                    painter.drawRect(x1, y1, x2 - x1, y2 - y1)
                    painter.end()

                self.processed_label.setPixmap(processed_pixmap.scaled(
                    self.processed_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def convert_pil_to_pixmap(self, img):
        """将PIL图像转换为QPixmap"""
        img_byte_arr = BytesIO()
        img.save(img_byte_arr, format='PNG')
        qimg = QImage()
        qimg.loadFromData(img_byte_arr.getvalue())
        return QPixmap.fromImage(qimg)

    def reset_image(self):
        """重置图像到原始状态"""
        if self.original_img:
            self.processed_img = self.original_img.copy()
            self.current_img = self.processed_img.copy()
            self.reset_color_settings()
            self.update_image_display()
            self.statusBar().showMessage("已重置图像")
            # 重置UI控件状态
            self.operations_combo.setCurrentIndex(0)
            self.rotation_input.setText("0")
            self.scale_input.setText("100")
            self.cropping = False
            self.crop_start = QPoint()
            self.crop_end = QPoint()
            self.crop_button.setEnabled(True)
            self.apply_crop_button.setEnabled(False)

    def save_image(self):
        """保存图像"""
        if not self.current_img:
            return

        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存图像", "", "PNG文件 (*.png);;JPEG文件 (*.jpg);;所有文件 (*)", options=options
        )

        if file_path:
            try:
                self.current_img.save(file_path)
                self.statusBar().showMessage(f"已保存到: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "保存失败", f"保存图像时出错: {str(e)}")

    def on_shapes_operation_changed(self, index):
        """当形状识别操作改变时显示或隐藏相关参数控件"""
        operation = self.shapes_combo.currentText()
        if operation in ["检测圆形", "检测矩形", "检测三角形", "检测多边形", "自动检测所有"]:
            self.shapes_widget.show()
            self.detect_shapes()
        else:
            self.shapes_widget.hide()
            # 恢复原始图像显示
            self.current_img = self.processed_img.copy()
            self.apply_color_adjustments()

    def detect_shapes(self):
        """检测图像中的形状"""
        if not self.processed_img or self.shapes_combo.currentText() == "关闭":
            self.current_img = self.processed_img.copy()
            self.apply_color_adjustments()
            return

        # 转换为OpenCV格式
        img_cv = cv2.cvtColor(np.array(self.processed_img), cv2.COLOR_RGB2BGR)
        img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)

        # 应用阈值处理
        threshold = self.shape_threshold_slider.value()
        _, thresh = cv2.threshold(img_gray, threshold, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

        # 查找轮廓
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 创建用于绘制的图像副本
        img_result = img_cv.copy()

        # 根据选择的形状类型进行检测
        shape_type = self.shapes_combo.currentText()

        for contour in contours:
            # 计算轮廓面积，过滤小面积的轮廓
            area = cv2.contourArea(contour)
            if area < 100:  # 可调整的阈值
                continue

            # 计算轮廓的周长
            perimeter = cv2.arcLength(contour, True)

            # 多边形逼近
            epsilon = 0.04 * perimeter  # 可调整的参数
            approx = cv2.approxPolyDP(contour, epsilon, True)

            # 检测形状
            shape_detected = False

            if shape_type == "检测圆形" or shape_type == "自动检测所有":
                # 检测圆形
                (x, y), radius = cv2.minEnclosingCircle(contour)
                center = (int(x), int(y))
                radius = int(radius)

                # 计算轮廓面积与圆面积的比率，判断是否为圆形
                circle_area = 3.14 * radius * radius
                area_ratio = area / circle_area

                if area_ratio > 0.7 and len(approx) > 6:  # 圆形的边数较多
                    cv2.circle(img_result, center, radius, (0, 255, 0), 2)
                    cv2.putText(img_result, "Circle", (int(x) - 20, int(y) - 20),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
                    shape_detected = True

            if (shape_type == "检测矩形" or shape_type == "自动检测所有") and not shape_detected:
                # 检测矩形
                if len(approx) == 4:
                    cv2.drawContours(img_result, [contour], 0, (0, 0, 255), 2)
                    cv2.putText(img_result, "Rectangle", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                    shape_detected = True

            if (shape_type == "检测三角形" or shape_type == "自动检测所有") and not shape_detected:
                # 检测三角形
                if len(approx) == 3:
                    cv2.drawContours(img_result, [contour], 0, (255, 0, 0), 2)
                    cv2.putText(img_result, "Triangle", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
                    shape_detected = True

            if (shape_type == "检测多边形" or shape_type == "自动检测所有") and not shape_detected:
                # 检测多边形（边数大于4）
                if len(approx) > 4:
                    cv2.drawContours(img_result, [contour], 0, (255, 255, 0), 2)
                    cv2.putText(img_result, f"Polygon ({len(approx)})", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)

        # 转回PIL格式
        self.current_img = Image.fromarray(cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB))
        self.apply_color_adjustments()
        self.statusBar().showMessage(f"已检测形状，阈值: {threshold}")

    def select_stitch_images(self):
        """选择要拼接的图像"""
        options = QFileDialog.Options()
        files, _ = QFileDialog.getOpenFileNames(
            self, "选择要拼接的图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp);;所有文件 (*)", options=options
        )

        if files:
            self.stitch_images = []
            for file in files:
                img = cv2.imread(file)
                if img is not None:
                    self.stitch_images.append(img)

            if len(self.stitch_images) >= 2:
                self.start_stitch_button.setEnabled(True)
                self.statusBar().showMessage(f"已选择 {len(self.stitch_images)} 张图像用于拼接")
            else:
                QMessageBox.warning(self, "图像数量不足", "至少需要选择2张图像进行拼接")
                self.start_stitch_button.setEnabled(False)

    def perform_stitching(self):
        """执行图像拼接"""
        if len(self.stitch_images) < 2:
            QMessageBox.warning(self, "图像数量不足", "至少需要2张图像进行拼接")
            return

        # 使用OpenCV的Stitcher进行拼接
        status, stitched = self.stitcher.stitch(self.stitch_images)

        if status == cv2.Stitcher_OK:
            # 转换为PIL格式
            stitched_rgb = cv2.cvtColor(stitched, cv2.COLOR_BGR2RGB)
            self.processed_img = Image.fromarray(stitched_rgb)
            self.current_img = self.processed_img.copy()

            # 更新显示
            self.update_image_display()
            self.statusBar().showMessage(f"图像拼接成功，已生成全景图")

            # 重置UI状态
            self.operations_combo.setCurrentIndex(0)
            self.reset_color_settings()
        else:
            QMessageBox.critical(self, "拼接失败", f"图像拼接失败，错误代码: {status}")
            self.statusBar().showMessage("图像拼接失败")

    # 视频处理相关方法
    def select_video_file(self):
        """选择视频文件"""
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)", options=options
        )

        if file_path:
            self.video_file_label.setText(os.path.basename(file_path))
            self.video_capture = cv2.VideoCapture(file_path)

            if not self.video_capture.isOpened():
                QMessageBox.critical(self, "错误", "无法打开视频文件")
                return

            # 获取视频信息
            self.total_frames = int(self.video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
            self.video_fps = self.video_capture.get(cv2.CAP_PROP_FPS)

            # 更新时间标签
            total_seconds = self.total_frames / self.video_fps
            total_minutes = int(total_seconds // 60)
            total_seconds = int(total_seconds % 60)
            self.time_label.setText(f"00:00/{total_minutes:02d}:{total_seconds:02d}")

            # 更新进度条范围
            self.video_progress.setRange(0, self.total_frames)

            # 读取并显示第一帧
            ret, frame = self.video_capture.read()
            if ret:
                self.display_video_frame(frame, frame)

            # 启用控制按钮
            self.play_button.setEnabled(True)
            self.pause_button.setEnabled(False)
            self.stop_button.setEnabled(False)

            self.statusBar().showMessage(f"已加载视频: {os.path.basename(file_path)}")

    def play_video(self):
        """播放视频"""
        if not self.video_capture or self.is_playing:
            return

        self.is_playing = True
        self.play_button.setEnabled(False)
        self.pause_button.setEnabled(True)
        self.stop_button.setEnabled(True)

        # 启动定时器
        self.video_timer.start(int(1000 / self.video_fps))  # 以视频帧率更新
        self.statusBar().showMessage("播放中...")

    def pause_video(self):
        """暂停视频"""
        if not self.is_playing:
            return

        self.is_playing = False
        self.play_button.setEnabled(True)
        self.pause_button.setEnabled(False)
        self.stop_button.setEnabled(True)

        # 停止定时器
        self.video_timer.stop()
        self.statusBar().showMessage("已暂停")

    def stop_video(self):
        """停止视频"""
        if not self.video_capture:
            return

        self.is_playing = False
        self.play_button.setEnabled(True)
        self.pause_button.setEnabled(False)
        self.stop_button.setEnabled(False)

        # 停止定时器
        self.video_timer.stop()

        # 重置到视频开始位置
        self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
        self.current_frame = 0
        self.video_progress.setValue(0)

        # 更新时间标签
        total_seconds = self.total_frames / self.video_fps
        total_minutes = int(total_seconds // 60)
        total_seconds = int(total_seconds % 60)
        self.time_label.setText(f"00:00/{total_minutes:02d}:{total_seconds:02d}")

        # 读取并显示第一帧
        ret, frame = self.video_capture.read()
        if ret:
            self.display_video_frame(frame, frame)

        self.statusBar().showMessage("已停止")

    def forward_video(self):
        """快进视频"""
        if not self.video_capture:
            return

        # 快进10秒
        forward_frames = int(10 * self.video_fps)
        current_pos = self.video_capture.get(cv2.CAP_PROP_POS_FRAMES)
        new_pos = min(current_pos + forward_frames, self.total_frames - 1)

        self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, new_pos)
        self.current_frame = int(new_pos)
        self.video_progress.setValue(self.current_frame)

        # 更新时间标签
        current_seconds = self.current_frame / self.video_fps
        current_minutes = int(current_seconds // 60)
        current_seconds = int(current_seconds % 60)
        total_seconds = self.total_frames / self.video_fps
        total_minutes = int(total_seconds // 60)
        total_seconds = int(total_seconds % 60)
        self.time_label.setText(f"{current_minutes:02d}:{current_seconds:02d}/{total_minutes:02d}:{total_seconds:02d}")

        # 读取并显示当前帧
        ret, frame = self.video_capture.read()
        if ret:
            processed_frame = self.process_video_frame(frame)
            self.display_video_frame(frame, processed_frame)

    def backward_video(self):
        """快退视频"""
        if not self.video_capture:
            return

        # 快退10秒
        backward_frames = int(10 * self.video_fps)
        current_pos = self.video_capture.get(cv2.CAP_PROP_POS_FRAMES)
        new_pos = max(current_pos - backward_frames, 0)

        self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, new_pos)
        self.current_frame = int(new_pos)
        self.video_progress.setValue(self.current_frame)

        # 更新时间标签
        current_seconds = self.current_frame / self.video_fps
        current_minutes = int(current_seconds // 60)
        current_seconds = int(current_seconds % 60)
        total_seconds = self.total_frames / self.video_fps
        total_minutes = int(total_seconds // 60)
        total_seconds = int(total_seconds % 60)
        self.time_label.setText(f"{current_minutes:02d}:{current_seconds:02d}/{total_minutes:02d}:{total_seconds:02d}")

        # 读取并显示当前帧
        ret, frame = self.video_capture.read()
        if ret:
            processed_frame = self.process_video_frame(frame)
            self.display_video_frame(frame, processed_frame)

    def set_video_position(self, position):
        """设置视频位置"""
        if not self.video_capture:
            return

        self.current_frame = position
        self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, position)

        # 更新时间标签
        current_seconds = self.current_frame / self.video_fps
        current_minutes = int(current_seconds // 60)
        current_seconds = int(current_seconds % 60)
        total_seconds = self.total_frames / self.video_fps
        total_minutes = int(total_seconds // 60)
        total_seconds = int(total_seconds % 60)
        self.time_label.setText(f"{current_minutes:02d}:{current_seconds:02d}/{total_minutes:02d}:{total_seconds:02d}")

        # 读取并显示当前帧
        ret, frame = self.video_capture.read()
        if ret:
            processed_frame = self.process_video_frame(frame)
            self.display_video_frame(frame, processed_frame)

    def set_video_processing(self, index):
        """设置视频处理类型"""
        self.video_processing_type = self.video_processing_combo.currentText()

        # 显示或隐藏参数控件
        if self.video_processing_type == "形状识别":
            self.video_shape_widget.show()
        else:
            self.video_shape_widget.hide()

    def set_video_shape_threshold(self, value):
        """设置视频形状识别阈值"""
        self.video_shape_threshold_value = value
        self.video_shape_threshold_value.setText(str(value))

    def process_video_frame(self, frame):
        """处理视频帧"""
        if self.video_processing_type == "无处理":
            return frame

        elif self.video_processing_type == "灰度图":
            return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        elif self.video_processing_type == "边缘检测":
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            return cv2.Canny(gray, 100, 200)

        elif self.video_processing_type == "形状识别":
            # 转换为灰度图
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

            # 应用阈值处理
            _, thresh = cv2.threshold(gray, self.video_shape_threshold_value, 255,
                                      cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

            # 查找轮廓
            contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

            # 创建用于绘制的图像副本
            result = frame.copy()

            for contour in contours:
                # 计算轮廓面积，过滤小面积的轮廓
                area = cv2.contourArea(contour)
                if area < 100:  # 可调整的阈值
                    continue

                # 计算轮廓的周长
                perimeter = cv2.arcLength(contour, True)

                # 多边形逼近
                epsilon = 0.04 * perimeter  # 可调整的参数
                approx = cv2.approxPolyDP(contour, epsilon, True)

                # 检测圆形
                (x, y), radius = cv2.minEnclosingCircle(contour)
                center = (int(x), int(y))
                radius = int(radius)

                # 计算轮廓面积与圆面积的比率，判断是否为圆形
                circle_area = 3.14 * radius * radius
                area_ratio = area / circle_area

                if area_ratio > 0.7 and len(approx) > 6:  # 圆形的边数较多
                    cv2.circle(result, center, radius, (0, 255, 0), 2)
                    cv2.putText(result, "Circle", (int(x) - 20, int(y) - 20),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
                elif len(approx) == 4:  # 矩形
                    cv2.drawContours(result, [contour], 0, (0, 0, 255), 2)
                    cv2.putText(result, "Rectangle", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                elif len(approx) == 3:  # 三角形
                    cv2.drawContours(result, [contour], 0, (255, 0, 0), 2)
                    cv2.putText(result, "Triangle", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
                else:  # 多边形
                    cv2.drawContours(result, [contour], 0, (255, 255, 0), 2)
                    cv2.putText(result, f"Polygon ({len(approx)})", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)

            return result

        return frame

    def update_video_frame(self):
        """更新视频帧"""
        if not self.video_capture:
            return

        ret, frame = self.video_capture.read()

        if ret:
            self.current_frame = int(self.video_capture.get(cv2.CAP_PROP_POS_FRAMES))
            self.video_progress.setValue(self.current_frame)

            # 更新时间标签
            current_seconds = self.current_frame / self.video_fps
            current_minutes = int(current_seconds // 60)
            current_seconds = int(current_seconds % 60)
            total_seconds = self.total_frames / self.video_fps
            total_minutes = int(total_seconds // 60)
            total_seconds = int(total_seconds % 60)
            self.time_label.setText(
                f"{current_minutes:02d}:{current_seconds:02d}/{total_minutes:02d}:{total_seconds:02d}")

            # 处理帧
            processed_frame = self.process_video_frame(frame)

            # 显示帧
            self.display_video_frame(frame, processed_frame)
        else:
            # 视频结束，停止播放
            self.stop_video()

    def display_video_frame(self, original_frame, processed_frame):
        """显示视频帧"""
        # 转换原始帧为QPixmap
        if len(original_frame.shape) == 3:  # 彩色图像
            rgb_image = cv2.cvtColor(original_frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        else:  # 灰度图像
            h, w = processed_frame.shape
            q_img = QImage(processed_frame.data, w, h, w, QImage.Format_Grayscale8)

        original_pixmap = QPixmap.fromImage(q_img)
        self.video_original_label.setPixmap(original_pixmap.scaled(
            self.video_original_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

        # 转换处理后的帧为QPixmap
        if len(processed_frame.shape) == 3:  # 彩色图像
            rgb_image = cv2.cvtColor(processed_frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        else:  # 灰度图像
            h, w = processed_frame.shape
            q_img = QImage(processed_frame.data, w, h, w, QImage.Format_Grayscale8)

        processed_pixmap = QPixmap.fromImage(q_img)
        self.video_processed_label.setPixmap(processed_pixmap.scaled(
            self.video_processed_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    # 摄像头处理相关方法
    def change_camera(self, index):
        """更改摄像头"""
        self.camera_id = index
        if self.is_camera_active:
            self.stop_camera()
            self.start_camera()

    def start_camera(self):
        """启动摄像头"""
        if self.is_camera_active:
            return

        self.camera_capture = cv2.VideoCapture(self.camera_id)

        if not self.camera_capture.isOpened():
            QMessageBox.critical(self, "错误", f"无法打开摄像头 {self.camera_id}")
            return

        self.is_camera_active = True
        self.start_camera_button.setEnabled(False)
        self.stop_camera_button.setEnabled(True)

        # 启动定时器
        self.camera_timer.start(30)  # 约30fps
        self.statusBar().showMessage(f"摄像头 {self.camera_id} 已启动")

    def stop_camera(self):
        """停止摄像头"""
        if not self.is_camera_active:
            return

        self.is_camera_active = False
        self.start_camera_button.setEnabled(True)
        self.stop_camera_button.setEnabled(False)

        # 停止定时器
        self.camera_timer.stop()

        # 释放摄像头资源
        if self.camera_capture:
            self.camera_capture.release()
            self.camera_capture = None

        # 清空显示
        self.camera_original_label.clear()
        self.camera_processed_label.clear()

        self.statusBar().showMessage("摄像头已停止")

    def set_camera_processing(self, index):
        """设置摄像头处理类型"""
        self.camera_processing_type = self.camera_processing_combo.currentText()

        # 显示或隐藏参数控件
        if self.camera_processing_type == "形状识别":
            self.camera_shape_widget.show()
            self.camera_face_widget.hide()
        elif self.camera_processing_type == "人脸检测":
            self.camera_shape_widget.hide()
            self.camera_face_widget.show()
        else:
            self.camera_shape_widget.hide()
            self.camera_face_widget.hide()

    def set_camera_shape_threshold(self, value):
        """设置摄像头形状识别阈值"""
        self.camera_shape_threshold_value = value
        self.camera_shape_threshold_value.setText(str(value))

    def set_camera_face_scale(self, value):
        """设置人脸检测缩放因子"""
        self.camera_face_scale_value = value / 10.0
        self.camera_face_scale_value.setText(f"{self.camera_face_scale_value:.1f}")

    def set_camera_face_neighbors(self, value):
        """设置人脸检测邻居数"""
        self.camera_face_neighbors_value = value
        self.camera_face_neighbors_value.setText(str(value))

    def process_camera_frame(self, frame):
        """处理摄像头帧"""
        if self.camera_processing_type == "无处理":
            return frame

        elif self.camera_processing_type == "灰度图":
            return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        elif self.camera_processing_type == "边缘检测":
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            return cv2.Canny(gray, 100, 200)

        elif self.camera_processing_type == "形状识别":
            # 转换为灰度图
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

            # 应用阈值处理
            _, thresh = cv2.threshold(gray, self.camera_shape_threshold_value, 255,
                                      cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

            # 查找轮廓
            contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

            # 创建用于绘制的图像副本
            result = frame.copy()

            for contour in contours:
                # 计算轮廓面积，过滤小面积的轮廓
                area = cv2.contourArea(contour)
                if area < 100:  # 可调整的阈值
                    continue

                # 计算轮廓的周长
                perimeter = cv2.arcLength(contour, True)

                # 多边形逼近
                epsilon = 0.04 * perimeter  # 可调整的参数
                approx = cv2.approxPolyDP(contour, epsilon, True)

                # 检测圆形
                (x, y), radius = cv2.minEnclosingCircle(contour)
                center = (int(x), int(y))
                radius = int(radius)

                # 计算轮廓面积与圆面积的比率，判断是否为圆形
                circle_area = 3.14 * radius * radius
                area_ratio = area / circle_area

                if area_ratio > 0.7 and len(approx) > 6:  # 圆形的边数较多
                    cv2.circle(result, center, radius, (0, 255, 0), 2)
                    cv2.putText(result, "Circle", (int(x) - 20, int(y) - 20),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
                elif len(approx) == 4:  # 矩形
                    cv2.drawContours(result, [contour], 0, (0, 0, 255), 2)
                    cv2.putText(result, "Rectangle", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                elif len(approx) == 3:  # 三角形
                    cv2.drawContours(result, [contour], 0, (255, 0, 0), 2)
                    cv2.putText(result, "Triangle", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
                else:  # 多边形
                    cv2.drawContours(result, [contour], 0, (255, 255, 0), 2)
                    cv2.putText(result, f"Polygon ({len(approx)})", (approx[0][0][0], approx[0][0][1] - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)

            return result

        elif self.camera_processing_type == "人脸检测":
            # 转换为灰度图
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

            # 检测人脸
            faces = self.face_cascade.detectMultiScale(
                gray,
                scaleFactor=self.camera_face_scale_value,
                minNeighbors=self.camera_face_neighbors_value,
                minSize=(30, 30),
                flags=cv2.CASCADE_SCALE_IMAGE
            )

            # 在原图上绘制人脸框
            result = frame.copy()
            for (x, y, w, h) in faces:
                cv2.rectangle(result, (x, y), (x + w, y + h), (0, 255, 0), 2)
                cv2.putText(result, "Face", (x, y - 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

            return result

        return frame

    def update_camera_frame(self):
        """更新摄像头帧"""
        if not self.is_camera_active or not self.camera_capture:
            return

        ret, frame = self.camera_capture.read()

        if ret:
            # 处理帧
            processed_frame = self.process_camera_frame(frame)

            # 显示帧
            self.display_camera_frame(frame, processed_frame)

    def display_camera_frame(self, original_frame, processed_frame):
        """显示摄像头帧"""
        # 转换原始帧为QPixmap
        if len(original_frame.shape) == 3:  # 彩色图像
            rgb_image = cv2.cvtColor(original_frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        else:  # 灰度图像
            h, w = processed_frame.shape
            q_img = QImage(processed_frame.data, w, h, w, QImage.Format_Grayscale8)

        original_pixmap = QPixmap.fromImage(q_img)
        self.camera_original_label.setPixmap(original_pixmap.scaled(
            self.camera_original_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

        # 转换处理后的帧为QPixmap
        if len(processed_frame.shape) == 3:  # 彩色图像
            rgb_image = cv2.cvtColor(processed_frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        else:  # 灰度图像
            h, w = processed_frame.shape
            q_img = QImage(processed_frame.data, w, h, w, QImage.Format_Grayscale8)

        processed_pixmap = QPixmap.fromImage(q_img)
        self.camera_processed_label.setPixmap(processed_pixmap.scaled(
            self.camera_processed_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def show_about(self):
        """显示关于对话框"""
        QMessageBox.about(self, "关于", """
图像处理与视频分析工具 v1.0

这是一个功能强大的图像处理和视频分析工具，支持多种图像处理操作、视频播放和摄像头实时处理。

主要功能:
- 图像处理: 亮度/对比度/饱和度调整、灰度图、黑白图、模糊、锐化、边缘检测等
- 形状识别: 检测圆形、矩形、三角形和多边形
- 图像拼接: 多张图像自动拼接成全景图
- 视频处理: 视频播放、暂停、停止、快进、快退等
- 视频效果: 灰度、边缘检测、形状识别
- 摄像头处理: 实时显示摄像头画面并应用各种效果
- 人脸检测: 实时检测人脸并标记

使用方法:
1. 打开图像或视频文件进行处理
2. 选择不同的处理选项进行操作
3. 调整参数以获得最佳效果
4. 保存处理后的图像或视频

作者: doubao
""")


if __name__ == "__main__":
    # 确保中文显示正常
    import matplotlib

    matplotlib.use('Agg')  # 使用Agg后端，不显示图形界面

    app = QApplication(sys.argv)
    # 设置应用样式
    app.setStyle('Fusion')

    window = VideoProcessor()
    window.show()

    sys.exit(app.exec_())